package ui.server;

import java.util.ArrayList;
import java.util.HashMap;

import ui.client.Group;
import ui.client.InstantMessage;
import ui.client.UserPresence;





/**
 * the class of ConnectionManager is used to manage 
 * all the XMPPConnectionManager and mapping to the sessionid
 * USING SINGLETON PATTERN
 * @author wangkaidi
 *
 */
public class ConnectionManager 
{
	private static ConnectionManager cm;
	private ConnectionManager()
	{		
		SimpleLogger.getInstance().setLevel(SimpleLogger.FATAL);
	}
	
	/**
	 *
	 * @return the only instance
	 */
	public static synchronized ConnectionManager getInstance()
	{
		if(cm==null)
		{
			cm=new ConnectionManager();
		}
		return cm;
	}
	
	private HashMap<String,AbstractProtocolManager> map=new HashMap<String,AbstractProtocolManager>();//store the mapping of sessionids and XMPPProtocolManager
	private HashMap<String,ArrayList<InstantMessage>>messagemap=new HashMap<String,ArrayList<InstantMessage>>();//store the messages and sessionid mapping
	private HashMap<String,ArrayList<UserPresence>>presencemap=new HashMap<String,ArrayList<UserPresence>>();//store the UserPresence and sessionid mapping
	
	/**
	 * return whether specific session id has 
	 * corresponding XMPPProcotolManager
	 * @param sessionid
	 */
	public synchronized boolean exists(String sessionid)
	{
		return map.containsKey(sessionid);
	}
	
	/**
	 * return the XMPPProtocolManager which the session id maps
	 * @param sessionid
	 * @return if not existed ,null will be returned
	 */
	public synchronized AbstractProtocolManager getManager(String sessionid)
	{
		return map.get(sessionid);
	}
	
	/**
	 * create an XMPPProtocolManager associate with the session id
	 * @param sessionid
	 */
	public synchronized void createManager(String protocolname,String username,String password,String domain,String host,int port,String sessionid)
	{
		if(protocolname.equals("XMPP"))
		{
			AbstractProtocolManager manager=new XMPPProtocolManager(username,password,domain,host,port);
			MyEventHandler handler=new MyEventHandler(sessionid);//create a Eventhandler
			manager.setEventHandler(handler);
			
			map.put(sessionid, manager);
			messagemap.put(sessionid, new ArrayList<InstantMessage>());
			presencemap.put(sessionid, new ArrayList<UserPresence>());//put the session and ProtocolManager to the HashMap
		}
		else//if not found 
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not create protocolmanager for \""+protocolname+"\"");
		}
	}
	
	/**
	 * return whether specific session id 
	 * has incoming message
	 * @param sessionid
	 * @return
	 */
	public synchronized boolean hasMessages(String sessionid)
	{
		return messagemap.get(sessionid).size()>0;
	}
	
	/**
	 * return a list of InstantMessage of Specific session id.
	 * it will create a copy of  the list of Message 
	 * and CLEAR THE ORIGINAL MESSAGE LIST
	 * NOTICE THERE IS NO NEED TO CLEAR IT MANULLY
	 * @param sessionid
	 * @return
	 */
	public synchronized ArrayList<InstantMessage> getMessages(String sessionid)
	{
		ArrayList<InstantMessage>list=messagemap.get(sessionid);
		ArrayList<InstantMessage>clone=new ArrayList<InstantMessage>();
		for(int i=0;i<list.size();i++)
		{
			clone.add(list.get(i));
		}
		list.clear();//clear the original list
		return clone;
	}
	
	/**
	 * send message in specific sessionid
	 * @param sessionid
	 * @param message
	 */
	public synchronized boolean sendMessage(String sessionid,InstantMessage message)
	{
		AbstractProtocolManager manager=this.getManager(sessionid);
		if(manager==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find the ProtocolManager for session :"+sessionid);
			return false;
		}
		else
		{
			SimpleLogger.getInstance().log(SimpleLogger.DEBUG,"send message : "+message.toString());
			InstantMessage im=new InstantMessage(message.getFromID(),message.getDestinationID(),message.getContent());
			try
			{
				manager.sendMessage(im);
				return true;
			}
			catch(Exception e)
			{
				SimpleLogger.getInstance().log(SimpleLogger.WARNING,e.toString());
				return false;
			}
		}
	}	
	
	public synchronized ArrayList<Group> getGroup(String sessionid)
	{
		AbstractProtocolManager manager=this.getManager(sessionid);
		if(manager==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find the ProtocolManager for session :"+sessionid);
			return null;
		}
		else
		{
			SimpleLogger.getInstance().log(SimpleLogger.DEBUG, "get groups: "+sessionid);
			ArrayList<Group> groups=manager.getGroups();			
			return groups;
		}
	}
	
	/**
	 * disconnect the protocolmanager with specific sessionid
	 * @param sessionid
	 */
	public synchronized void disconnect(String sessionid)
	{
		AbstractProtocolManager manager=this.getManager(sessionid);
		if(manager==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find the ProtocolManager for session :"+sessionid);
		}
		manager.close();//close the protocolmanager
		map.remove(sessionid);
		messagemap.remove(sessionid);
		presencemap.remove(sessionid);
		//remove the map entry in the HashMap "map" and "messagemap";
	}
	
	/**
	 * this method will add a MyMessage object to messagemap.
	 * @param sessionid
	 * @param message
	 */
	public synchronized void addMessage(String sessionid,InstantMessage message)
	{
		SimpleLogger.getInstance().log(SimpleLogger.DEBUG, "receive a message : "+message.getContent());
		AbstractProtocolManager manager=this.getManager(sessionid);
		if(manager==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find the ProtocolManager for session :"+sessionid);
			return;
		}
		ArrayList<InstantMessage>list=messagemap.get(sessionid);
		if(list==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find Message list for session :" +sessionid);
			return;
		}
		list.add(message);
	}
	
	/**
	 * this method return whether specific session has UserPresence change info
	 * @param sessionid
	 * @return
	 */
	public synchronized boolean isPresenceChange(String sessionid)
	{
		return presencemap.get(sessionid).size()>0;
	}
	
	/**
	 * this method return a list of UserPresence
	 * @param sessionid
	 * @return
	 */
	public synchronized ArrayList<UserPresence> getPresence(String sessionid)
	{
		ArrayList<UserPresence>list=presencemap.get(sessionid);
		ArrayList<UserPresence>temp=new ArrayList<UserPresence>();
		for(int i=0;i<list.size();i++)
		{
			temp.add(list.get(i));
		}
		list.clear();
		return temp;
	}
	
	/**
	 * this method will add a UserPresence to the presencemap
	 * @param sessionid
	 * @param presence
	 */
	public synchronized void addPresence(String sessionid,UserPresence presence)
	{
		SimpleLogger.getInstance().log(SimpleLogger.DEBUG,"receive a Presence "+presence.getUserID()+":"+presence.getStatus());
		AbstractProtocolManager manager=this.getManager(sessionid);
		if(manager==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find the ProtocolManager for session :"+sessionid);
			return;
		}
		ArrayList<UserPresence>list=presencemap.get(sessionid);
		if(list==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find Presence list for session :" +sessionid);
			return;
		}
		list.add(presence);
	}
	
	/**
	 * login to the server 
	 * @param sessionid
	 * @return
	 */
	public synchronized boolean login(String sessionid)
	{
		SimpleLogger.getInstance().log(SimpleLogger.DEBUG, "login sessionid is :" +sessionid);
		AbstractProtocolManager manager=this.getManager(sessionid);
		if(manager==null)
		{
			SimpleLogger.getInstance().log(SimpleLogger.FATAL, "can not find the ProtocolManager for session :"+sessionid);
			return false;
		}
		else
		{
			try
			{
				manager.connect();
				manager.login();
				return true;
			}
			catch(Exception e)
			{
				SimpleLogger.getInstance().log(SimpleLogger.WARNING,e.toString());					
				return false;
			}
		}	
	}
}
